package nsl.com.util.excel.complex;


import nsl.com.util.excel.basic.UtilExcelCell;
import nsl.com.util.excel.basic.UtilExcelCoordinateConverter;
import nsl.com.util.excel.basic.UtilExcelLocate;
import nsl.com.util.excel.basic.UtilExcelWorkBookAndSheet;
import nsl.com.util.excel.bean.ExcelPoint;
import nsl.com.util.excel.bean.SheetHead;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * 由汇总表信息，生成多个分表信息.
 * 使用改工具类时，必须有汇总信息表和模版分表.
 * 1. 汇总信息表：a.主要有汇总信息sheet,名字根据类变量SUMMARY_SHEET_NAME指定.默认Summary.
 *                b.汇总表和分表对应关系的配置信息sheet,名字根据类变量LOCATION_INOF_SHEET_NAME,默认Location_Info
 * 2. 模版分表: 将汇总表中的每一行信息，映射到分表上，其中sheet名称必须制定为:Sheet1
 */
public class SumFile2SubFile {

    //logger
    private static Logger logger= LoggerFactory.getLogger(SumFile2SubFile.class);

    // 位置信息sheet名称
     private static String LOCATION_INOF_SHEET_NAME="Location_Info";
    // 汇总信息sheet名称
    private static String SUMMARY_SHEET_NAME="Summary";
    //分表详单sheet名称
    private static  String SUB_DETAIL_SHEET_NAME="Sheet1";

    //表头信息
    private SheetHead sheetHead;

    //汇总表 abspath,要求汇总表有：有汇总信息必须有2个sheet:
    //1. Summary: 保存汇总的一条条记录；其中有一列为文件保存名
    //2. Location_Info: 保存汇总表和分表 地址对应信息；有以下要求：
    // a. 表头必须和汇总表一致; b.表头中一列可以制定为保存的文件名
    private String sumFileAbsPath;

    //汇总表中，Summary sheet中的标识 保存文件名所在的列
    private String columnNameOfSavedFileName;

    //模版详表absPath, 要求模板在 "Sheet1"中定义
    private String subFileAbsPath;

    //输出数据的目的地文件夹
    private String outputDir;

    //保存 位置对应信息
    //<汇总表列序号,详单地址> <1:B2>；key: 是汇总表中 取值列所在的序号(A列即为0)，value: 是汇总表该列信息在分表中具体位置信息(A3)
    private Map<Integer,String> locationInfoMap=new HashMap<>();

    //保存 分表中的一行的信息
    // 某一行信息中： <详表中内容的地址：详表中的Cell 内容>  <B2 : 李庄基站>
    //key: 分表中的位置; value:该位置中的值
    private Map<ExcelPoint,String> recordMap=new HashMap<>();

    /**
     * 文件地址为绝对地址的构造函数.
     * 所有入参必须不为空.
     * @param sheetHead
     * @param sumFileAbsPath
     * @param columnNameOfSavedFileName 文件保存名所在列的字母名称(A;B);
     * @param detailFileAbsPath
     * @param outputDir 绝对地址
     */
    public SumFile2SubFile(SheetHead sheetHead, String sumFileAbsPath, String columnNameOfSavedFileName, String detailFileAbsPath, String outputDir) {
        if(sheetHead==null || sumFileAbsPath ==null || columnNameOfSavedFileName==null || detailFileAbsPath==null || outputDir==null){
            throw new RuntimeException("构造函数中有 参数为空。");
        }
        this.sheetHead = sheetHead;
        this.sumFileAbsPath = sumFileAbsPath;
        this.columnNameOfSavedFileName = columnNameOfSavedFileName;
        this.subFileAbsPath = detailFileAbsPath;
        this.outputDir = outputDir;
    }

    /**
     * 构造函数,根据最后boolean型标志位,判断传入的文件目录是绝对目录还是相对于ClassPath的目录.
     * @param sheetHead
     * @param sumFilePath
     * @param columnNameOfSavedFileName 文件保存名所在列的字母名称(A;B);
     * @param subFilePath
     * @param outputDir 绝对地址
     * @param isAbsPath true:为绝对目录,false:为相对ClassPath的相对目录
     */
    public SumFile2SubFile(SheetHead sheetHead, String sumFilePath, String columnNameOfSavedFileName, String subFilePath, String outputDir, boolean isAbsPath) throws URISyntaxException {
        if(sheetHead==null || sumFilePath ==null || columnNameOfSavedFileName==null || subFilePath==null || outputDir==null){
            throw new RuntimeException("构造函数中有 参数为空。");
        }
        String sumFileAbsPath;
        String subFileAbsPath;
        if(!isAbsPath){
            URI uri1=new URI(SumFile2SubFile.class.getResource(sumFilePath).getPath().toString());
            URI uri2=new URI(SumFile2SubFile.class.getResource(subFilePath).getPath().toString());
            sumFileAbsPath= uri1.getPath();
            subFileAbsPath= uri2.getPath();

            logger.debug("sumFileAbsPath = "+ sumFileAbsPath);
            logger.debug("subFileAbsPath = "+ subFileAbsPath);
        }else {
            sumFileAbsPath=sumFilePath;
            subFileAbsPath=subFilePath;
        }
        this.sheetHead = sheetHead;
        this.sumFileAbsPath = sumFileAbsPath;
        this.columnNameOfSavedFileName = columnNameOfSavedFileName;
        this.subFileAbsPath = subFileAbsPath;
        this.outputDir = outputDir;

    }

    /**
     * 读取配置信息.
     * 根据汇总表中的配置sheet,讲汇总表同分表的 对应位置 信息读入locationInfoMap.
     * @throws IOException
     * @throws InvalidFormatException
     */
    //读取
    public void readLocationInfo() throws IOException, InvalidFormatException {
        Sheet locationInofSheet= UtilExcelWorkBookAndSheet.getSheet(sumFileAbsPath,LOCATION_INOF_SHEET_NAME);
        int locationInfoRowIndex=sheetHead.getBottom()+1;
        Row row=locationInofSheet.getRow(locationInfoRowIndex);
        Iterator<Cell> iterator=row.cellIterator();
        while (iterator.hasNext()){
            Cell cell=iterator.next();
            int index=cell.getColumnIndex();

            String cellString= UtilExcelCell.getCellStringValue(cell);
            cellString=cellString.replace("$","");
            //System.out.println(index+" = "+cellString);
            if(cellString.equalsIgnoreCase("") || cellString.equalsIgnoreCase("location")){
                continue;
            }
            ExcelPoint point= UtilExcelCoordinateConverter.convertCellAddressFromString2Point(cellString);
            //System.out.println(point);
            locationInfoMap.put(index,cellString);
        }
        logger.debug("locationInfoMap = "+locationInfoMap);
    }


    /**
     * 根据locationInfoMap,读取汇总表中的一行记录到map集合
     * @param summarySheet 汇总表sheet
     * @param recordRow 读取记录所在行号,起始为0
     * @return Map<ExcelPoint,String>:<分表位置,字符串值>
     */
    public Map<ExcelPoint,String> readOneRecord(Sheet summarySheet, int recordRow){
        //保存 分表中的一行的信息
        // 某一行信息中： <详表中内容的地址：详表中的Cell 内容>  <B2 : 李庄基站>
        //key: 分表中的位置; value:该位置中的值
        Map<ExcelPoint,String> recordMap=new HashMap<>();
        //int beginRow=4;
        Row row=summarySheet.getRow(recordRow);
        for(Map.Entry<Integer,String> entry:locationInfoMap.entrySet()){
            Integer columnIndex=entry.getKey();
            String locationString=entry.getValue();
            ExcelPoint recordPosition= UtilExcelCoordinateConverter.convertCellAddressFromString2Point(locationString);
            String cellContent="";
            Cell cell=row.getCell(columnIndex);
            if(cell!=null){
                cellContent= UtilExcelCell.getCellStringValue(cell);
            }
            recordMap.put(recordPosition,cellContent);
        }
        logger.debug("recordMap = "+recordMap);
        return recordMap;
    }

    /**
     * 从汇总Excel中的汇总sheet,读取文件名列所在的文件名
     * @param sheet 汇总表sheet
     * @param recordRow 读取记录所在行号,起始为0
     * @return 汇总表中的文件名(一般不包含后缀名)
     */
    public  String getSubFileName(Sheet sheet,int recordRow){
        Row row=sheet.getRow(recordRow);
        if(row==null){
            //
            throw new RuntimeException("行号空");
        }
        int colummIndex= UtilExcelCoordinateConverter.convertColumnAddressFromLetter2Index(columnNameOfSavedFileName);
        Cell cell=row.getCell(colummIndex);
        if(cell==null ){
            throw new RuntimeException("cell 为 空");
        }
        return UtilExcelCell.getCellStringValue(cell);
    }


    /**
     * 根据 分表模版;记录Map,填充模版excel文件,并以指定名称保存文件.
     * @param excelPointMap 数据记录Map
     * @param absPathOfSavedFile 保存文件完整路径
     * @param workbookOfTemplate 模版workbook
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    public void writeSubFile(Map<ExcelPoint,String> excelPointMap, String absPathOfSavedFile, Workbook workbookOfTemplate) throws InvocationTargetException, IllegalAccessException, IOException {
        Sheet sheet=workbookOfTemplate.getSheet(SUB_DETAIL_SHEET_NAME);
        //Assert.assertNotNull(sheet);
        for(Map.Entry<ExcelPoint,String> entry:excelPointMap.entrySet()){
            ExcelPoint point= entry.getKey();
            String value=entry.getValue();
            UtilExcelCell.setCellWithStringValue(sheet,point,value);
        }
        // Write the output to a file
        FileOutputStream fileOut = new FileOutputStream(absPathOfSavedFile);
        workbookOfTemplate.write(fileOut);
        fileOut.close();
    }

    public void run() throws IOException, InvalidFormatException, InvocationTargetException, IllegalAccessException {
        //1.读取配置信息
        readLocationInfo();
        //2.获取分表中的文件扩展名:xls or xlsx;
        int lastIndex=subFileAbsPath.lastIndexOf(".");
        logger.debug(lastIndex+"");
        String suffix= subFileAbsPath.substring(lastIndex,subFileAbsPath.length());
        logger.debug(suffix);
        //3.逐条读取汇总表中的信息,并写入文件

        Sheet sumSheet= UtilExcelWorkBookAndSheet.getSheet(sumFileAbsPath,SUMMARY_SHEET_NAME);

        int lastRowIndex= UtilExcelLocate.findMaxValidatedRow(sumSheet,sheetHead);
        int beginRowIndex=sheetHead.getBottom()+1;
        logger.debug("beginRowIndex = "+beginRowIndex);
        logger.debug("lastRowIndex = "+lastRowIndex);

        for(int i=beginRowIndex;i<=lastRowIndex;i++){
            Workbook subTemplateWorkbook= UtilExcelWorkBookAndSheet.getWorkbook(subFileAbsPath);
            Map<ExcelPoint,String> recordMap=readOneRecord(sumSheet,i);
            String fileName=getSubFileName(sumSheet,i);
            //System.out.println(recordMap);
            //System.out.println(fileName);
            fileName=outputDir+fileName+"."+suffix;
            //System.out.println(fileName);
            logger.debug("fileName = "+fileName );
            writeSubFile(recordMap,fileName,subTemplateWorkbook);
        }
    }
}
